﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Smart.Model
{
    [ObjectSystem]
    public class ObjectPoolAwakeSystem : AwakeSystem<ObjectPool, EventSystem,Transform>
    {
        public override void Awake(ObjectPool self, EventSystem eventSystem,Transform parent)
        {
            self.Awake(eventSystem,parent);
        }
    }

    public interface IPooledOnPut
    {
        void OnPut();
    }

    public interface IPooledOnGet
    {
        void OnGet();
    }

    public interface IPooledDestroy
    {
        void OnDestroy();
    }

    public class ObjectPool : ComponentView
    {
        private readonly Dictionary<Type, Queue<Component>> dictionary = new Dictionary<Type, Queue<Component>>();
        private readonly Dictionary<Type, Queue<object>> datas = new Dictionary<Type, Queue<object>>();

        public override void Awake(EventSystem eventSystem, Transform parent)
        {
            base.Awake(eventSystem, parent);
            gameObject.name = "组件对象池";
        }

        public override void Dispose()
        {
            if(!this.Disposed)
            {
                this.Clear();
            }
            base.Dispose();
        }

        public Component Get(Type type)
        {
            Component obj;
            if (!this.dictionary.TryGetValue(type, out Queue<Component> queue))
            {
                obj = (Component)Activator.CreateInstance(type);
            }
            else if (queue.Count == 0)
            {
                obj = (Component)Activator.CreateInstance(type);
            }
            else
            {
                obj = queue.Dequeue();
            }

            obj.IsFromPool = true;
            if(obj is IPooledOnGet pooledOnGet)
            {
                pooledOnGet.OnGet();
            }

            return obj;
        }

        public object GetData(Type type)
        {
            object obj;
            if (!this.datas.TryGetValue(type, out Queue<object> queue))
            {
                obj = (object)Activator.CreateInstance(type);
            }
            else if (queue.Count == 0)
            {
                obj = (object)Activator.CreateInstance(type);
            }
            else
            {
                obj = queue.Dequeue();
            }

            if (obj is IPooledOnGet pooledOnGet)
            {
                pooledOnGet.OnGet();
            }

            return obj;
        }

        public T Get<T>() where T : Component
        {
            T t = (T)this.Get(typeof(T));
            return t;
        }

        public void Put(Component obj)
        {
            Type type = obj.GetType();
            if (!this.dictionary.TryGetValue(type, out Queue<Component> queue))
            {
                queue = new Queue<Component>(32);
                this.dictionary.Add(type, queue);
            }

            if(obj is IPooledOnPut pooledOnPut)
            {
                pooledOnPut.OnPut();
            }

            queue.Enqueue(obj);
        }

        public T GetData<T>() where T : class,new()
        {
            return this.GetData(typeof(T)) as T;
        }

        public void PutData(object data)
        {
            Type type = data.GetType();
            if (!this.datas.TryGetValue(type, out Queue<object> queue))
            {
                queue = new Queue<object>(32);
                this.datas.Add(type, queue);
            }

            if (data is IPooledOnPut pooledOnPut)
            {
                pooledOnPut.OnPut();
            }

            queue.Enqueue(data);
        }

        public void Clear()
        {
            foreach (var kv in this.dictionary)
            {
                var queue = kv.Value;
                while (queue.Count > 0)
                {
                    var component = queue.Dequeue();
                    component.IsFromPool = false;
                    component.Dispose();
                }
            }
            this.dictionary.Clear();

            foreach (var kv in this.datas)
            {
                var queue = kv.Value;
                while (queue.Count > 0)
                {
                    var data = queue.Dequeue();
                    if(data is IPooledDestroy pooledDestroy)
                    {
                        pooledDestroy.OnDestroy();
                    }
                }
            }
            this.datas.Clear();
        }
    }
}